Naver Open API
Products -> 서비스 API -> 검색 (https://developers.naver.com/products/service-api/search/search.md)
We are not using .csv file toady.
API (Application Programming Interface): Mechanism or system enabling two software components to communicate using a set of definitions and protocols. (https://aws.amazon.com/what-is/api/ https://brunch.co.kr/@jhw28/31)
Let’s look at the address of CAU Notice page.
We are using NAVER Open API today. To use an API, you have to read the API reference. It’s sort of a dictionary.
https://developers.naver.com/docs/serviceapi/search/news/news.md#%EB%89%B4%EC%8A%A4
XML (Extensible Markup Language): Language used to define and store data in a sharable manner. ( https://aws.amazon.com/what-is/xml/?nc1=h_ls, https://www.tcpschool.com/xml/xml_basic_structure)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(httr)
library(XML)
Select your word to search.
keyword <- "검색하고싶으은문자열을넣으세요"
Using API, we need to communicate with NAVER the But, URL doesnt not understand 한글. Just like the NAVER API reference document address above, it should be in Percent Encoding. “%” sign does not mean percentage. https://www.youtube.com/watch?v=3DNaj8R4HJg&t=37s
(https://it-eldorado.tistory.com/143, https://bunny.net/academy/http/what-is-url-uniform-resource-identifier-and-percent-encoding/)
myQuery <- keyword %>%
enc2utf8() %>%
URLencode()
print(myQuery)
## [1] "%EC%97%AC%ED%96%89"
We are making an order to API server. 1) language: percent encoding. 2) vocabulary: read the reference.
searchUrl <- "https://openapi.naver.com/v1/search/news.xml"
chunk <- 50
startNumber <-1
sort<- "sim"
url <- glue::glue("{searchUrl}?query={myQuery}&display={chunk}&start={startNumber}&sort={sort}")
url
## https://openapi.naver.com/v1/search/news.xml?query=%EC%97%AC%ED%96%89&display=50&start=1&sort=sim
GET(url, add_header(information to request) : retrieves
the page with the url
doc_raw <- url %>%
GET(
add_headers(
"X-Naver-Client-Id" = client_id,
"X-Naver-Client-Secret" = client_secret
)
) %>%
toString() %>%
XML::xmlParse()
Let’s see how doc_raw looks like.
It’s well organized but hard to read. Let’s make it into a dataframe. One row for one article.
doc_df<- doc_raw %>%
getNodeSet("//item") %>%
xmlToDataFrame()
head(doc_df)
As NAVER News search result shows my keyword in bold, the reuslt also
has <b></b> html tags. We really do not neet
it. Let’s remove it.
For that, we are using regular expression, which is also a way of present letters/numbers/punctuation and stuff.
https://www.youtube.com/shorts/kGFUu5WAzd8
library(stringr)
doc_df%>%mutate(title_text = str_remove_all(
title, "&\\w+;|<[[:punct:]]*b>"))
Similarly, let’s define a function to refine the texts.
get_list <- function(doc) {
refined<- doc %>%
getNodeSet("//item") %>%
xmlToDataFrame() %>%
rename("publish_date" = pubDate) %>%
mutate(publish_date = as.POSIXct(publish_date,
format = "%a, %d %b %Y %H:%M:%S %z")) %>%
mutate(title_text = str_replace_all(
title, "&\\w+;|<[[:punct:]]*b>", " ")) %>%
mutate(title_text = str_replace_all(
title_text, "([[:punct:]])", " ")) %>%
mutate(title_text = str_replace_all(
title_text, "\\s{2,}", " ")) %>%
mutate(title_text = str_remove_all(
title_text, "\\A\\s+")) %>%
mutate(description_text = str_remove_all(
description,
"&\\w+;|<[[:punct:]]*b>|[“”]"))
return(refined)
}
get_list(doc_raw)
Let’s say we want to get 100 result for “첫눈” and 500 results for “채용”. What do we do when repeating same process? Use a function :)
NNSearch <- function(keyword = NULL, chunk = 100, start = 1,
sort = c("date", "sim"), do_done = FALSE, max = 1000L,
client_id = client_id, client_secret = client_secret,
verbose = TRUE) {
if (is.null(keyword)) {
stop("Error: Input search keyword")
}
if (chunk < 1 & chunk > 100) {
stop("Error: Chunk size should be 1 < chunk <=100")
}
if (start < 1 & start > 1000) {
stop("Error: Start should be 1< start <=1000.")
}
sort <- match.arg(sort)
searchUrl <- "https://openapi.naver.com/v1/search/news.xml"
query <- keyword %>%
enc2utf8() %>%
URLencode()
url <- glue::glue("{searchUrl}?query={query}&display={chunk}&start={start}&sort={sort}")
doc <- url %>%
GET(
add_headers(
"X-Naver-Client-Id" = client_id,
"X-Naver-Client-Secret" = client_secret
)
) %>%
toString() %>%
XML::xmlParse()
totalNaverResult <- doc %>%
XML::getNodeSet("//total") %>%
XML::xmlValue() %>%
as.integer()
if (!do_done){
max <- chunk
}
if (verbose) {
glue::glue("* [{keyword}] 뉴스 검색 결과: 총 {totalNaverResult}건.\n\n") %>%
cat()
glue::glue(" - ({chunk}/{min(totalNaverResult, max)})건 완료.\n\n") %>%
cat()
}
search_list <- doc %>%
get_list()
nRecord <- NROW(search_list)
if (!do_done | nRecord >= totalNaverResult | nRecord >= max) {
return(search_list)
} else {
totalMyResult <- min(totalNaverResult, max)
pageCount <- totalMyResult %/% chunk
if (totalMyResult %/% chunk == 0) {
pageCount <- pageCount - 1
}
idx <- (seq(pageCount-1)+1)
add_list <- idx[idx <= 1000] %>%
purrr::map_df({
function(x) {
if (verbose) {
glue::glue(" - ({chunk * x}/{totalMyResult})건 .\n\n") %>%
cat()
}
glue::glue(
"{searchUrl}?query={query}&display={chunk}&start={x}&sort={sort}"
) %>%
httr::GET(
httr::add_headers(
"X-Naver-Client-Id" = client_id,
"X-Naver-Client-Secret" = client_secret
)
) %>%
toString() %>%
XML::xmlParse() %>%
get_list()
}
})
search_list %>%
bind_rows(
add_list
) %>%
return()
}
}
firstsnow<- NNSearch("첫눈", client_id = client_id, client_secret = client_secret,
sort="sim", max=100, do_done=T)
## * [첫눈] 뉴스 검색 결과: 총 120463건.
## - (100/100)건 완료.
hiring_sim<- NNSearch("채용", client_id = client_id, client_secret = client_secret,
sort="sim", max=1000, do_done=T)
## * [채용] 뉴스 검색 결과: 총 2730527건.
## - (100/1000)건 완료.
## - (200/1000)건 .
## - (300/1000)건 .
## - (400/1000)건 .
## - (500/1000)건 .
## - (600/1000)건 .
## - (700/1000)건 .
## - (800/1000)건 .
## - (900/1000)건 .
## - (1000/1000)건 .
hiring_date<- NNSearch("채용", client_id = client_id, client_secret = client_secret,
sort="date", max=1000, do_done=T)
## * [채용] 뉴스 검색 결과: 총 2730527건.
## - (100/1000)건 완료.
## - (200/1000)건 .
## - (300/1000)건 .
## - (400/1000)건 .
## - (500/1000)건 .
## - (600/1000)건 .
## - (700/1000)건 .
## - (800/1000)건 .
## - (900/1000)건 .
## - (1000/1000)건 .
library(tm)
## Loading required package: NLP
##
## Attaching package: 'NLP'
## The following object is masked from 'package:httr':
##
## content
#devtools::install_github('haven-jeon/KoNLP')
library(KoNLP)
## Checking user defined dictionary!
useSejongDic()
## Backup was just finished!
## 370957 words dictionary was built.
Noun extraction
sentence<- "과일 비싸다고 안 사 먹으면 죽어서 제사상에서나 먹겠지. 인생 별거 있나 나에게 남은 여름은 100번도 안 된다."
extractNoun(sentence)
## [1] "과일" "제사상" "인생" "별거" "나" "남" "여름" "100"
## [9] "번"
break down by morpheme
tidytext::unnest_tokens(dataframe, new column, source column, criteria)
break down text data by token (vowel / consonant / letter / morpheme /
word / n-gram /..)
#install.packages("tidytext")
library(tidytext)
hiring_date_word<- hiring_date %>%
unnest_tokens(title_word, title_text, "words")
hiring_date_word
hiring_date_pos <-hiring_date%>% unnest_tokens(pos, title_text, SimplePos09) %>%
group_by(originallink) %>%
mutate(pos_order = 1:n())
hiring_date_pos
Let’s take nouns only.
hiring_date_pos2<- hiring_date_pos %>%
filter(str_detect(pos, "/n")) %>%
mutate(pos_done = str_remove(pos, "/n.*$")) %>%
mutate(pos_done = str_remove(pos_done, ".*\\+(?=.)"))
hiring_date_pos2
hiring_date_pos2$pos_done%>%table%>%sort(decreasing=T)
## .
## 모 연속 집
## 202 144 143
## 지역사회공헌 인정제 코레일
## 137 134 127
## 2년 최고등급 티웨이항공
## 124 107 79
## 멘토링 공 추진단장
## 75 69 69
## 올해 5기 객실승무원
## 61 60 60
## 에이블스쿨 용 채
## 60 59 59
## 프로젝트 한국형 개
## 59 59 52
## 채용 최 운
## 52 52 50
## 데 동문 중앙대
## 48 48 48
## 현직 프로그램 2기
## 48 47 43
## 대학생 브루킨즈 싱크탱크
## 43 43 43
## 아카데미 2023년 내년
## 43 42 42
## 복지부 실 (주)디에스테크노와
## 42 41 40
## 12월 강진군 소재
## 40 40 40
## 신입 외국 음성군
## 40 40 40
## 한양증권 시험 9급
## 33 32 30
## 건설 결 공무원
## 30 30 30
## 공장 급 도입
## 30 30 30
## 디지털 이차전지 제주
## 30 30 30
## 청년 최고등 투자협약
## 30 30 30
## 내달 다빈치캠 인턴
## 29 28 27
## 평 여주시 일자리드림데이
## 22 21 21
## 1500억 4번 4회
## 20 20 20
## 7일 것 계절
## 20 20 20
## 계절근로자 고용부 공개
## 20 20 20
## 공공형 국어 근로제
## 20 20 20
## 기업 남자 네번
## 20 20 20
## 다빈치캠퍼스 대상 등
## 20 20 20
## 박람회 번째 베트남
## 20 20 20
## 새만금 수 수원시
## 20 20 20
## 아내 양성 어
## 20 20 20
## 은평구 이재명 인재
## 20 20 20
## 일자리 접 진
## 20 20 20
## 추 출제기조 투
## 20 20 20
## 해커톤 객실 승무원
## 20 19 19
## 료 성 의왕도시공사
## 18 18 17
## 체험형 신규 3월
## 17 16 13
## 남 내 일정
## 12 12 12
## 코딩능력 표현 마지막
## 12 12 11
## 1 102명 16조
## 10 10 10
## 17년 1조1600억 2024년
## 10 10 10
## 2024전기 2025년 27
## 10 10 10
## 29 30 35세
## 10 10 10
## hds1 가능성 가문
## 10 10 10
## 가치 간담회 감
## 10 10 10
## 감소 개선 개원식
## 10 10 10
## 개최 거 거부
## 10 10 10
## 경 경영 경쟁
## 10 10 10
## 계약학과 고용노동부 고용불안
## 10 10 10
## 고용창출 고용회복 공격
## 10 10 10
## 공모 공모전 공채
## 10 10 10
## 공포 구인 구직
## 10 10 10
## 구직자 구청 국가
## 10 10 10
## 국가필수선박 국산화 국제우호도시
## 10 10 10
## 규모 그랜드코리아레저 근로자용
## 10 10 10
## 근로활동 금단 기
## 10 10 10
## 기업잇는 날 남편
## 10 10 10
## 노란봉투법 노인생활지원사 뉴스라이브
## 10 10 10
## 단독 단속반 대구시
## 10 10 10
## 대규모 돌풍 동계
## 10 10 10
## 동아대 디렉터 디스플레이
## 10 10 10
## 디지털인재 람 마
## 10 10 10
## 명분 목표주 몸싸움
## 10 10 10
## 문장가들 문집 물류24시
## 10 10 10
## 물리검층 민간 바다
## 10 10 10
## 박사 반도체 반복
## 10 10 10
## 발 발전소 방송
## 10 10 10
## 방송3법 배토작업 벌
## 10 10 10
## 변 별거 보건난제
## 10 10 10
## 보건의료고등연구계획 불법투기 사
## 10 10 10
## 사관학교 사람 사례
## 10 10 10
## 사이버 상 상당수
## 10 10 10
## 상임감정위원 새만금에 선
## 10 10 10
## 설 성공 성본산단
## 10 10 10
## 성황리 세 세미나
## 10 10 10
## 수요 수요데이 숙소
## 10 10 10
## 시추공 신당 신입생
## 10 10 10
## 신입ᆞ경력 신청 실무
## 10 10 10
## 심 심부 심화
## 10 10 10
## 쓰레기 아이디어 안드로이드
## 10 10 10
## 안전 애 애정행각
## 10 10 10
## 양질 어르신일자리 어르신일자리박람회
## 10 10 10
## 없 엠오티 연
## 10 10 10
## 연간 영어 영어→현장직무
## 10 10 10
## 영업이익 영역 오늘
## 10 10 10
## 옥천군 온라인 외국인력
## 10 10 10
## 용납 우수기업 원진
## 10 10 10
## 유경준 유통업계 은
## 10 10 10
## 은평어르신일자리 의 의료분쟁조정중재원
## 10 10 10
## 이 이낙연 이사회
## 10 10 10
## 익산지청 인식 인재양성
## 10 10 10
## 일반대학원 입장문 장비
## 10 10 10
## 전문가 전주시 제
## 10 10 10
## 제조사 조명 종목
## 10 10 10
## 줄 중 중국
## 10 10 10
## 중소기업 중심 증가
## 10 10 10
## 지역 지연 지질자원연
## 10 10 10
## 진행 참여 채씨
## 10 10 10
## 첫 체결 초고령
## 10 10 10
## 최일수 추가 추정치
## 10 10 10
## 충북도 취업 취업역량
## 10 10 10
## 칼바 케이프 쾌커
## 10 10 10
## 투자협 트렐릭스 패러다임
## 10 10 10
## 패션비즈 평강 풍힙현과
## 10 10 10
## 플랜트 하츠타 학생
## 10 10 10
## 학술대회 한 한국esg경영인증원
## 10 10 10
## 한국교통대 한국철도공사 한양證
## 10 10 10
## 해결 해결사 행
## 10 10 10
## 행사 향 현대차그룹
## 10 10 10
## 현직자 혐오 협약
## 10 10 10
## 협의이혼 호 화웨이
## 10 10 10
## 확 확대 활용
## 10 10 10
## 회복 훙멍시대 희망
## 10 10 10
## 희망퇴직 尹 尹정부
## 10 10 10
## 外 7대 강원교육
## 10 9 8
## 도교육청 콜센터 고용촉진간담회
## 8 8 7
## 충북북부보훈지청 글 금융사
## 7 6 6
## 란 모델 블라인드
## 6 6 6
## 서린씨앤아이 시리즈 실무자
## 6 6 6
## 에센코어 여대 용량
## 6 6 6
## 크라스 클레브 6기
## 6 6 5
## 강원교육콜센터 강원도교육청 만트럭버스코리아
## 5 5 5
## 아우스빌둥 출범식 기업들
## 5 5 4
## 뒷조사 래 뭐길
## 4 4 4
## 사이 서비스 유행
## 4 4 4
## 일본 취준생 5
## 4 4 3
## 레벨 토론하자 e와글
## 3 3 2
## 광고 남성혐오 논란⋯
## 2 2 2
## 누리꾼들 무 속
## 2 2 2
## 영상 유럽 포스코
## 2 2 2
## dn솔루션즈 공략 독일에
## 1 1 1
## 센터 시장 테크니컬
## 1 1 1
## 픈
## 1
hiring_date_freq<- hiring_date_pos2 %>%
ungroup() %>% count(pos_done, sort=T) %>% rename(noun=pos_done)
hiring_date_freq
library(ggplot2)
##
## Attaching package: 'ggplot2'
## The following object is masked from 'package:NLP':
##
## annotate
hiring_date_freq %>% ggplot(aes(x=noun, y=n)) +
geom_bar(stat = "identity")
hiring_date_freq%>%slice_max(n,n=30)
hiring_date_freq%>%filter(n>=30)%>% ggplot(aes(x=noun, y=n)) +
geom_bar(stat = "identity", aes(fill=n)) +
theme(axis.text.x = element_text(angle = 90))
theme_set(theme_grey(base_family='NanumGothic'))
hiring_date_freq%>%filter(n>=30)%>% ggplot(aes(x=noun, y=n)) +
geom_bar(stat = "identity", aes(fill=n)) +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Well……?
What about this?
#install.packages("wordcloud2")
library(wordcloud2)
library(RColorBrewer)
display.brewer.all()
library(wordcloud2)
wc_hiring_date<- wordcloud2(hiring_date_freq, fontFamily = "AppleGothic",
size=1,color = 'random-dark')
wc_hiring_date